package com.xiyuan.smartutils.template.engine.expression.operator;

import com.xiyuan.smartutils.Classes;
import com.xiyuan.smartutils.Types;
import com.xiyuan.smartutils.template.engine.VariableMap;
import com.xiyuan.smartutils.template.engine.exception.ExpressionException;
import com.xiyuan.smartutils.template.engine.expression.Expression;
import com.xiyuan.smartutils.template.engine.expression.primitive.*;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 赋值运算
 * 
 * @author lgz 2020年12月9日 新建与整理
 * 
 */
public class _Voluation extends Operator
{
    private static final long serialVersionUID = 1L;
    private Expression field;
    private Expression val;
    
    public _Voluation(String path, int line, int column, Expression field, Expression val)
    {
        super(path, line, column);
        this.field = field;
        this.val = val;
        this.assertSymbo("等号赋值", field, val);
    }
    
    public int getType()
    {
        return VOLUATION;
    }
    
    @Override
    public String toString()
    {
        return new StringBuilder(field.toString()).append(" = ").append(val).toString();
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected Object build(VariableMap variableMap)
    {
        if (field.getType() == Primitive.VARIABLE)
        {// 前面是变量
        
            if (field.getTarget() == null)
            {// 局部变量设置值
                String key = field.toString();
                if (!variableMap.has(key))
                    throw new ExpressionException(this, "赋值表达式赋值时变量\"" + key + "\"未定义");
                
                Object value = val.execute(variableMap);
                variableMap.setValue(key, value);
                return value;
            }
            
            String key = ((_Variable) field).getVariableName();
            Object obj = field.getTarget().execute(variableMap);
            Field prop = this.getField(obj, key);
            if (prop == null)
                throw new ExpressionException(this, "赋值表达式 {" + field + "}找不到属性“" + key + "”");
            
            Class<?> type = prop.getType();
            // 有对象属性情况下就要 严格要求类型相同
            Object value = val.execute(variableMap);
            if (Types.isPrimitive(type))
            {// 原型数据
            
                if (value == null)
                    throw new ExpressionException(this, "赋值表达式赋值时数值类型属性[\"" + key + "\"]不允许设置为null");
                
                if (!Types.isPrimitive(value))
                    throw new ExpressionException(this, "赋值表达式赋值时数值类型属性[\"" + key + "\"]要求为数值型");
                
                if (Types.isByte(type))
                    value = val.getType() == Primitive.INTEGER ? ((_Integer) val).getByte() : (byte) value;
                else if (Types.isShort(type))
                    value = val.getType() == Primitive.INTEGER ? ((_Integer) val).getShort() : (short) value;
                else if (Types.isInt(type))
                    value = val.getType() == Primitive.INTEGER ? ((_Integer) val).getInt() : (int) value;
                else if (Types.isLong(type))
                    value = val.getType() == Primitive.INTEGER ? ((_Integer) val).getLong() : (long) value;
                else if (Types.isFloat(type))
                    value = val.getType() == Primitive.DECIMAL ? ((_Decimal) val).getFloat() : (float) value;
                else if (Types.isDouble(type))
                    value = val.getType() == Primitive.DECIMAL ? ((_Decimal) val).getDouble() : (double) value;
                else if (Types.isChar(type))
                    value = val.getType() == Primitive.CHAR ? ((_Char) val).getValue() : (char) value;
                else if (Types.isBoolean(type))
                    value = val.getType() == Primitive.BOOLEAN ? ((_Boolean) val).getValue() : (boolean) value;
                
                Classes.setFieldValue(obj, prop, value);
                return value;
            }
            
            if (value != null && !type.isAssignableFrom(value.getClass()))
                throw new ExpressionException(this, "赋值表达式赋值时[" + val + "]结果不是属性[" + field + "]类型或者其子类");
            
            Classes.setFieldValue(obj, prop, value);
            return value;
        }
        else if (field.getType() == INDEXABLE && field.getTarget() != null)
        {// 目标对象数组｜列表｜MAP 赋值，严格要求类型相同 才能复制
        
            _Indexable _target = (_Indexable) field;
            Object obj = field.getTarget().execute(variableMap);
            if (obj == null)
                throw new ExpressionException(this, "赋值表达式赋值时对象或者数组表达式{" + this + "}源对象不允许为null");
            
            Object ind = _target.getKey().execute(variableMap);
            
            if (Types.isArray(obj))
            {
                
                if (!Types.isInteger(ind))
                    throw new ExpressionException(this, "赋值表达式赋值时索引表达式结果不是整型");
                
                if (((Number) ind).intValue() >= Array.getLength(obj))
                    throw new ArrayIndexOutOfBoundsException(((Number) ind).intValue());
                
                Object value = val.execute(variableMap);
                Class<?> valClass = obj.getClass().getComponentType();
                if (Types.isNumber(valClass) && value == null)// 验证数据类型
                    throw new ExpressionException(this, "赋值表达式赋值时结果不是数类型");
                
                if (Types.isPrimitive(valClass) && Types.isPrimitive(value))
                {// 原型数据
                    if (byte.class == valClass || valClass == Byte.class)
                        value = ((Number) value).byteValue();
                    else if (char.class == valClass || valClass == Character.class)
                        value = ((char) value);
                    else if (short.class == valClass || valClass == Short.class)
                        value = ((Number) value).shortValue();
                    else if (int.class == valClass || valClass == Integer.class)
                        value = ((Number) value).intValue();
                    else if (long.class == valClass || valClass == Long.class)
                        value = ((Number) value).longValue();
                    else if (float.class == valClass || valClass == Float.class)
                        value = ((Number) value).floatValue();
                    else if (double.class == valClass || valClass == Double.class)
                        value = ((Number) value).doubleValue();
                }
                else
                {// 检查数据类型
                    this.assertType(valClass, value == null ? Object.class : value.getClass());
                }
                
                Array.set(obj, ((Number) ind).intValue(), value);
                return value;
                
            }
            else if (obj instanceof List)
            {
                if (!Types.isInt(ind))
                    throw new ExpressionException(this, "赋值表达式赋值时索引表达式结果不是整型");
                
                if (((Number) ind).intValue() >= ((List<Object>) obj).size())
                    throw new ArrayIndexOutOfBoundsException(((Number) ind).intValue());
                
                Object value = val.execute(variableMap);
                
                // TODO 这里需要判断数据类型
                ((List<Object>) obj).set(((Number) ind).intValue(), value);
                return value;
            }
            else if (obj instanceof Set)
            {
                if (!Types.isInt(ind))
                    throw new ExpressionException(this, "赋值表达式赋值时索引表达式结果不是整型");
                Object value = val.execute(variableMap);
                // TODO 这里需要判断数据类型
                
                Set<Object> set = (Set<Object>) obj;
                set.add(value);// 添加一对象
                return value;
            }
            else if (obj instanceof Map)
            {
                
                Map<Object, Object> map = (Map<Object, Object>) obj;
                Object value = val.execute(variableMap);
                // TODO 这里需要判断数据类型
                
                if (map.containsKey(ind))
                    map.put(ind, value);
                
                return value;
            }
        }
        
        throw new ExpressionException(this, "赋值表达式只允许给变量|对象属性|数组|列表|map赋值");
    }
    
    private void assertType(Object type, Object value) throws ExpressionException
    {
        
    }
    
    private void assertType(Class<?> type, Class<?> value) throws ExpressionException
    {
        if (!type.isAssignableFrom(value))
            throw new ExpressionException(this, "赋值表达式{" + this + "}数据类型不匹配");
    }
}
